箭头函数与 this 面试题全解析
一、核心要点速览
💡 核心考点
- this 绑定: 箭头函数的 this 继承自定义时的上下文
- 与普通函数区别: 箭头函数没有自己的 this、arguments、super、new.target
- 适用场景: 回调函数、数组方法、保持 this 上下文
- 不适用场景: 对象方法、构造函数、需要动态 this 的场景
二、箭头函数基础语法
1. 基本用法
javascript
// 普通函数
function add(a, b) {
return a + b
}
// 箭头函数
const add = (a, b) => {
return a + b
}
// 简化写法(单表达式自动返回)
const add = (a, b) => a + b
// 单个参数省略括号
const double = x => x * 2
// 无参数
const sayHi = () => console.log('Hi')
// 返回对象(需要括号)
const getUser = () => ({ name: 'Vue', age: 3 })
// 多语句(需要花括号和 return)
const process = (data) => {
const result = transform(data)
return result
}2. 语法对比
javascript
// ========== 普通函数 ==========
function fn1(a, b) {
return a + b
}
// ========== 箭头函数 ==========
const fn2 = (a, b) => a + b
// ========== 默认参数 ==========
function fn3(a, b = 10) {
return a + b
}
const fn4 = (a, b = 10) => a + b
// ========== 剩余参数 ==========
function fn5(...args) {
return args.reduce((sum, val) => sum + val, 0)
}
const fn6 = (...args) => args.reduce((sum, val) => sum + val, 0)
// ========== 解构参数 ==========
function fn7({ name, age }) {
return `${name}, ${age}`
}
const fn8 = ({ name, age }) => `${name}, ${age}`三、this 绑定规则详解
1. 普通函数 vs 箭头函数
javascript
// 普通函数:this 指向调用者
const obj1 = {
name: 'obj1',
getName() {
return this.name
}
}
obj1.getName() // 'obj1' (this → obj1)
const fn = obj1.getName
fn() // undefined (this → window)
// 箭头函数:this 指向定义时的上下文
const obj2 = {
name: 'obj2',
getName: () => {
return this.name // this → window/undefined
}
}
obj2.getName() // undefined (this 指向全局)2. this 指向对比图
┌──────────────────────────────────────────────────────────┐
│ 箭头函数 vs 普通函数 │
└──────────────────────────────────────────────────────────┘
普通函数的 this:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
const obj = {
name: 'MyObj',
sayName: function() {
console.log(this.name)
}
}
obj.sayName() // 'MyObj' (this → obj)
const fn = obj.sayName
fn() // undefined (this → window)
setTimeout(fn, 100) // undefined (this → window)
this 绑定规则:
┌────────────────────────────────┐
│ 谁调用,this 指向谁 │
│ │
│ obj.sayName() → this = obj │
│ fn() → this = window │
│ new Fn() → this = 新实例 │
│ call/apply → this = 指定对象 │
└────────────────────────────────┘
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
箭头函数的 this:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
const obj = {
name: 'MyObj',
sayName: () => {
console.log(this.name)
}
}
obj.sayName() // undefined (this → window)
this 绑定规则:
┌────────────────────────────────┐
│ 箭头函数没有自己的 this │
│ 继承自定义时上下文的 this │
│ │
│ 在全局 → this = window │
│ 在对象 → this = 外层 this │
│ 在 class → this = 实例 │
└────────────────────────────────┘
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━3. 经典面试题
javascript
// 题目 1:普通函数
const obj1 = {
name: 'obj1',
getName() {
setTimeout(function() {
console.log(this.name) // undefined
}, 100)
}
}
obj1.getName()
// 原因:setTimeout 的回调中 this → window
// 题目 2:箭头函数
const obj2 = {
name: 'obj2',
getName() {
setTimeout(() => {
console.log(this.name) // 'obj2'
}, 100)
}
}
obj2.getName()
// 原因:箭头函数的 this 继承自 getName 方法(→ obj2)
// 题目 3:嵌套箭头函数
const obj3 = {
name: 'obj3',
getName() {
const fn = () => {
const inner = () => {
console.log(this.name)
}
inner()
}
fn()
}
}
obj3.getName() // 'obj3'
// 原因:所有箭头函数都继承自同一 this四、经典应用场景
1. 保持 this 上下文
javascript
// ✓ 场景 1: 定时器回调
class Timer {
constructor() {
this.seconds = 0
// 箭头函数保持 this 为实例
setInterval(() => {
this.seconds++ // this 正确指向 Timer 实例
console.log(this.seconds)
}, 1000)
}
// ✗ 错误示范
startBad() {
setInterval(function() {
this.seconds++ // this → window,错误!
}, 1000)
}
// ✓ 正确示范(非箭头函数方案)
startGood() {
const self = this
setInterval(function() {
self.seconds++ // 保存 this 引用
}, 1000)
}
}2. 数组方法
javascript
// ✓ 场景 2: 数组操作
[1, 2, 3].map(x => x * 2) // [2, 4, 6]
[1, 2, 3].filter(x => x > 1) // [2, 3]
[1, 2, 3].reduce((sum, x) => sum + x, 0) // 6
// ✓ 配合 this
class Calculator {
constructor(multiplier) {
this.multiplier = multiplier
}
multiply(arr) {
return arr.map(x => x * this.multiplier)
// this 正确指向 Calculator 实例
}
}
const calc = new Calculator(2)
calc.multiply([1, 2, 3]) // [2, 4, 6]3. DOM 事件处理
javascript
// ✓ 场景 3: 事件监听
class Button {
constructor(element) {
this.element = element
this.clickCount = 0
// 箭头函数保持 this
this.element.addEventListener('click', (e) => {
this.clickCount++
this.handleClick(e)
})
}
handleClick(e) {
console.log(`Clicked ${this.clickCount} times`)
}
}
// jQuery 风格
$('.button').on('click', (e) => {
this.doSomething() // this 指向外部
})4. Promise 链
javascript
// ✓ 场景 4: Promise 链式调用
class UserService {
getUser(id) {
return fetch(`/api/users/${id}`)
.then(res => res.json())
.then(user => {
console.log(`User: ${user.name}`)
return this.processUser(user)
// this 正确指向 UserService
})
.catch(err => {
console.error(this.errorMessage)
throw err
})
}
}五、不适用场景
1. 对象方法
javascript
// ✗ 错误:对象方法不要用箭头函数
const obj = {
name: 'obj',
greet: () => `Hello, ${this.name}` // this → window
}
obj.greet() // 'Hello, undefined'
// ✓ 正确:使用普通函数
const obj = {
name: 'obj',
greet() {
return `Hello, ${this.name}`
}
}
obj.greet() // 'Hello, obj'2. 构造函数
javascript
// ✗ 错误:箭头函数不能用作构造函数
const Person = (name) => {
this.name = name
}
const p = new Person('Vue') // TypeError: Person is not a constructor
// ✓ 正确:使用普通函数
function Person(name) {
this.name = name
}
const p = new Person('Vue')3. 原型方法
javascript
// ✗ 错误:原型方法用箭头函数
function Person(name) {
this.name = name
}
Person.prototype.sayHi = () => {
console.log(`Hi, I'm ${this.name}`) // this → window
}
const p = new Person('Vue')
p.sayHi() // "Hi, I'm undefined"
// ✓ 正确:使用普通函数
Person.prototype.sayHi = function() {
console.log(`Hi, I'm ${this.name}`)
}
p.sayHi() // "Hi, I'm Vue"4. arguments 对象
javascript
// ✗ 错误:箭头函数没有 arguments
const fn = () => {
console.log(arguments) // ReferenceError
}
fn(1, 2, 3)
// ✓ 解决方案 1:使用剩余参数
const fn = (...args) => {
console.log(args) // [1, 2, 3]
}
// ✓ 解决方案 2:使用普通函数
function fn() {
console.log(arguments) // Arguments(3) [1, 2, 3]
}六、箭头函数的限制
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
箭头函数不能用作:
1. ❌ 构造函数
const Person = (name) => { this.name = name }
new Person('Vue') // TypeError
2. ❌ 原型方法
Person.prototype.sayHi = () => {}
// this 不指向实例
3. ❌ arguments 对象
const fn = () => arguments
fn(1, 2, 3) // ReferenceError
4. ❌ yield 关键字
// 不能作为 Generator 函数
5. ❌ 动态 this 场景
- 对象方法
- 事件处理器(需要 this → element)
- 需要 call/apply/bind 的场景
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━七、面试标准回答
箭头函数是 ES6 引入的新函数形式,提供了更简洁的语法和不同的 this 绑定规则。
语法特点:
- 更简洁:省略 function 关键字、单参数省略括号、单表达式自动返回
- 没有自己的 this、arguments、super、new.target
- 不能作为构造函数,不能使用 yield
this 绑定规则是箭头函数的核心特性:
- 箭头函数没有自己的 this
- this 继承自定义时的上下文(词法作用域)
- 一旦定义,this 永远无法改变
与普通函数的区别:
- 普通函数:this 指向调用者,可以动态改变
- 箭头函数:this 在定义时确定,无法改变
适用场景:
- 回调函数(setTimeout、Promise.then)
- 数组方法(map、filter、reduce)
- 需要保持 this 上下文的场景
不适用场景:
- 对象方法(需要动态 this)
- 构造函数(不能用 new)
- 原型方法(this 不指向实例)
- 需要 arguments 的场景
实际项目中,我主要用箭头函数写回调和数组操作,对象方法和构造函数仍然使用普通函数。这样既享受了箭头函数的便利,又避免了 this 陷阱。
八、记忆口诀
箭头函数歌诀:
箭头函数真简洁,
function 关键字省略。
没有自己的 this,
词法作用域来继承。
回调数组最适合,
对象方法要不得。
构造函数不能用,
arguments 也消失!
this 绑定要记清:
普通函数看调用,
箭头函数看定义,
场景选择最关键!九、推荐资源
十、总结一句话
- 箭头函数: 简洁语法 + 词法 this = 回调函数首选 🎯
- this 绑定: 定义时确定 + 不可改变 = 避免 this 丢失 ✓
- 使用场景: 回调数组适合 + 对象方法不适合 = 根据场景选择 ⚖️